using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Xml;
using System.Drawing;

namespace Athena.Core
{
    
    /// <summary>
    /// A Part is a Node that represents a piece of DNA that codes for a basic function
    /// The Part has all the properties of a Node, but it also has two rates contained within it
    /// These rates are called PoPS_Left and PoPS_Right. They are Edges that represent the "current"
    /// flowing through the DNA. The Part also has a Direction (forward or reverse). PoPS returns
    /// either PoPS_Left or PoPS_Right, depending on Direction. 
    /// </summary>
    [Serializable()]
    public class Part : Node
    {
        public static int COUNT = 0;
        
        /// <summary>
        /// This class represents the "current" flowing through a Part (DNA)
        /// It is essentially an Edge with modifications to the Inputs, Outputs, and Parent properties
        /// A PoPSRate always has a Part that it resides in. 
        /// </summary>
        [Serializable()]
        public class PoPSRate : Edge
        {
            protected Part _ParentPart = null;

            /// <summary>
            /// The part that this PoPS belong with
            /// </summary>
            public Part ParentPart
            {
                get { return _ParentPart; }
                set { _ParentPart = value; }
            }

            /// <summary>
            /// The ID for a regulatory reaction is always the ID of the Part + "_PoPS"
            /// </summary>
            public override string ID
            {
                get
                {
                    return _ID;
                }
                set
                {
                    _ID = value;
                }
            }
            
            /// <summary>
            /// The Parent for this regulatory reaction is the same as the Part's parent that
            /// this PoPS belong in
            /// </summary>
            public override Item Parent
            {
                get
                {
                    //return ParentPart;
                    if (ParentPart != null)
                        return ParentPart.Parent;
                    else
                        return null;
                }
                set
                {
                    //if (ParentPart != null)
                    //    ParentPart.Parent = value;
                }
            }
            /*
            /// <summary>
            /// updates the list of modifiers to match the incoming nodes
            /// </summary>
            public override void Update()
            {
                //if (!AUTO_MODIFIERS) return;
                base.Update();
                if (ParentPart != null)
                {
                    List<Node> reactants = new List<Node>();
                    foreach (Edge e in ParentPart.IncomingEdges)
                    {
                        foreach (Node n in e.Reactants)
                        {
                            reactants.Add(n);
                            if (!_Modifiers.Contains(n))
                                AddModifier(n);
                        }
                    }
                    Node[] modifiers = Modifiers;
                    foreach (Node n in modifiers)
                    {
                        if (!reactants.Contains(n))
                            _Modifiers.Remove(n);
                    }
                }
            }
            */
            /// <summary>
            /// Set the regulation rate for this part. 
            /// </summary>
            public override string Rate
            {
                get
                {
                    return base.Rate;
                }
                set
                {                    
                    base.Rate = value;

                    if (ParentPart != null)
                    {
                        if (ParentPart.PoPS_Left == this)
                            ParentPart.PoPS_Left = this;
                        else
                            if (ParentPart.PoPS_Right == this)
                                ParentPart.PoPS_Right = this;
                            else
                                ParentPart.PoPS = this;

                        foreach (Edge e in ParentPart.IncomingEdges)
                        {
                            foreach (Node n in e.Reactants)
                                if (!_Modifiers.Contains(n))
                                    AddModifier(n);
                        }
                        foreach (Edge e in ParentPart.IncomingEdges)
                        {
                            if (e.Rate != _Rate)
                                e.Rate = _Rate;
                            e.CopyModifiers(this);
                        }
                        if (ParentPart.Parent is Module)
                            ((Module)ParentPart.Parent).UpdateParameters();

                        if (AUTO_MODIFIERS)
                            ParentPart.GenerateModifiers();
                    }
                }
            }

            /// <summary>
            /// Get the inputs for this gene regulation rate
            /// The inputs for gene regulation are the same as the modifiers
            /// You cannot change the inputs -- use Modifiers to change the inputs
            /// </summary>
            public override Node[] Reactants
            {
                get
                {
                    return Modifiers;
                }
                set
                {
                    
                }
            }

            /// <summary>
            /// The output for the gene regulation reaction is always the 
            /// Part that is being regulated
            /// </summary>
            public override Node[] Products
            {
                get
                {
                    Node[] nodes = new Node[1];
                    nodes[0] = ParentPart;
                    return nodes;
                }
                set
                {
                }
            }

            /// <summary>
            /// Always false. A PoPS is never a reaction
            /// </summary>
            public override bool IsReaction
            {
                get
                {
                    return false;
                }
            }

            /// <summary>
            /// Always true. 
            /// </summary>
            public override bool IsRegulation
            {
                get
                {
                    return true;
                }
            }
            /// <summary>
            /// PoPS do not have reactants, so this does nothing
            /// </summary>
            /// <param name="input"></param>
            public override void AddReactant(Node input)
            {
            }
            /// <summary>
            /// PoPS do not have reactants, so this does nothing
            /// </summary>
            /// <param name="input"></param>
            public override void AddReactant(Node input, double stoichiometry)
            {
            }
            /// <summary>
            /// PoPS do not have products, so this does nothing
            /// </summary>
            /// <param name="input"></param>
            public override void AddProduct(Node output)
            {
            }
            /// <summary>
            /// PoPS do not have products, so this does nothing
            /// </summary>
            /// <param name="input"></param>
            public override void AddProduct(Node output, double stoichiometry)
            {
            }

            public PoPSRate(Part part) : base() 
            { 
                ParentPart = part; _Rate = "0";
                //if (part.IncomingEdges != null)
               //     foreach (Node n in part.IncomingEdges)
                 //       AddModifier(n);
            }
            public PoPSRate(Part part, string rate) : base(rate) 
            { 
                ParentPart = part;
               // if (part.IncomingEdges != null)
                //    foreach (Node n in part.IncomingEdges)
                 //       AddModifier(n);
            }
        }
        private bool generating = false;
        /// <summary>
        /// Looks at the modifiers of the PoPS rate and makes sure that those
        /// nodes are also included in the IncomingEdges of this part
        /// This method also modifies the Drawable
        /// </summary>
        public virtual void GenerateModifiers()
        {
            if (generating) return;

            if (_Left != null && _PoPS_Right == _Left._PoPS_Right && Direction == Orientation.forward) return;
            if (_Right != null && _PoPS_Left == _Right._PoPS_Left && Direction == Orientation.reverse) return;

            generating = true;

            Node[] popsModifiers = PoPS.Modifiers;
            if (popsModifiers == null) return;

            List<Node> newnodes = new List<Node>();
            List<Edge> oldedges = new List<Edge>();

            foreach (Edge edge in _IncomingEdges)
            {
                if (edge != null)
                {
                    List<Node> reactants = new List<Node>(edge.Reactants);
                    foreach (Node id in reactants)
                    {
                        if (id != null)
                        {
                            bool extra = true;
                            foreach (Node n in popsModifiers)
                            {
                                if (n != null && id == n)
                                {
                                    extra = false;
                                    break;
                                }
                            }
                            if (extra) oldedges.Add(edge); //excess
                        }
                    }
                }
            }

            foreach (Node n in popsModifiers)
            {
                bool contains = false;
                foreach (Edge edge in _IncomingEdges)
                {
                    if (edge != null && n != null)
                    {
                        List<Node> reactants = new List<Node>(edge.Reactants);
                        if (reactants.Contains(n))
                        {
                            contains = true;
                        }
                    }
                }
                if (!contains && !newnodes.Contains(n))
                    newnodes.Add(n);
            }

            foreach (Edge c in oldedges)
            {
                c.Remove();
                if (c.Drawable != null) c.Drawable.Remove();
            }
            foreach (Node n in newnodes)
            {
                Edge edge = new Edge(n, this, PoPS.Rate);
            }

            generating = false;
        }

        public override void Mirror(Item other)
        {
            if (other is Part)
            {
                Part copy = (Part)other;
                Position = copy._Position;
                Direction = copy._Direction;
                PoPS_Left = copy.PoPS_Left;
                _PoPS_Left.ParentPart = this;
                PoPS_Right = copy.PoPS_Right;
                _PoPS_Right.ParentPart = this;
                Sequence = copy.Sequence;
            }
        }

        public override void GenerateDrawable(System.Drawing.PointF point)
        {
            Drawable = new Objects.PartGlyph(point);            
            ((Objects.PartGlyph)_Drawable).GenerateModifiers();
        }

        public override object Clone()
        {
            return new Part(this);
        }

        public Part(Part copy)
            : base(copy)
        {
            Position = copy._Position;
            Direction = copy._Direction;
            PoPS_Left = copy.PoPS_Left;
            PoPS_Right = copy.PoPS_Right;
            Sequence = copy.Sequence;
            _AllowPoPSFlow = copy._AllowPoPSFlow;            
            ++COUNT;
        }

        protected int _Position;
        protected Part _Left, _Right;
        protected PoPSRate _PoPS_Left, _PoPS_Right;
       
        public enum Orientation { reverse, forward };
        protected Orientation _Direction;
        protected bool _AllowPoPSFlow = true;

        /// <summary>
        /// Sets whether or not the PoPS current can be passed to this part from neighboring
        /// parts
        /// </summary>
        protected virtual bool AllowPoPSFlow(Orientation direction)
        {
            return _AllowPoPSFlow || Direction != direction;
        }

        public PartDescription Description = new PartDescription();

        public Part()
        {
            _Direction = Orientation.forward;   //-1 is reverse, 1 is forward
            _Position = 0;
            Sequence = ("ACGT");            
            _PoPS_Left = new PoPSRate(this,"0");
            _PoPS_Right = new PoPSRate(this, "0");
            ++COUNT;
        }

        public Part(string type, string id, string seq)
            : base(type, id)
        {
            _Direction = Orientation.forward;   //-1 is reverse, 1 is forward
                        
            Sequence = (seq);
            _PoPS_Left = new PoPSRate(this, "0");
            _PoPS_Right = new PoPSRate(this, "0");
            ++COUNT;
        }

        private string _Sequence = "";
        public virtual string Sequence
        {
            get
            {
                return _Sequence;
            }
            set
            {
                _Sequence = value;
            }
        }
        /// <summary>
        /// This is used to obtain the DNA part that is directly to the left of this DNA part. Setting the Left of a DNA part will also
        /// alter the Right of the other DNA part
        /// </summary>
        public virtual Part Left
        {
            get 
            { 
                return _Left; 
            }
            set
            {
                if (value == _Left || value == this) return;
                if (value != null && value.Left == this) return;
                if (value == RightMostGlobal) return;

                if (_Left != null)
                {
                    if (_PoPS_Right == _Left.PoPS_Right)
                        PoPS_Right = (new PoPSRate(this));        //clear PoPS
                    if (_PoPS_Left == _Left.PoPS_Left)
                        _Left.PoPS_Left = (new PoPSRate(_Left));        //clear PoPS
                    _Left._Right = null;
                }

                _Left = value;

                if (_Left != null && _Left != this)
                {
                    _Left.Right = (this);
                    if (_Left.Right != this)
                    {
                        _Left = null;
                    }
                    else
                    {
                        if (_PoPS_Right.RateIsZero)
                            PoPS_Right = (_Left.PoPS_Right);

                        if (_Left.PoPS_Left.RateIsZero)
                            _Left.PoPS_Left = (PoPS_Left);
                    }
                }
            }
        }
        /// <summary>
        /// This is used to obtain the DNA part that is directly to the right of this DNA part. Setting the Right of a DNA part will also
        /// alter the Left of the other DNA part
        /// </summary>
        public virtual Part Right
        {
            get
            {
                return _Right;
            }
            set
            {
                if (value == _Right || value == this) return;
                if (value != null && value.Right == this) return;
                if (value == LeftMostGlobal) return;

                if (_Right != null)
                {
                    if (_PoPS_Left == _Right.PoPS_Left)
                        PoPS_Left = (new PoPSRate(this));        //clear PoPS
                    if (_PoPS_Right == _Right.PoPS_Right)
                        _Right.PoPS_Right = (new PoPSRate(_Right));        //clear PoPS
                    _Right._Left = null;    //important
                }

                _Right = value;

                if (_Right != null && _Right != this)
                {
                    _Right.Left = (this);
                    if (_Right.Left != this)
                    {
                        _Right = null;
                    }
                    else
                    {
                        if (_Right.PoPS_Right.RateIsZero)
                            _Right.PoPS_Right = (PoPS_Right);  //propagate PoPS to the neighbor

                        if (_PoPS_Left.RateIsZero)
                            PoPS_Left = (_Right.PoPS_Left);   //get PoPS from the neighbor
                    }
                }
            }
        }
        /// <summary>
        /// Returns the left-most part such that that part is still contained within this part's module
        /// </summary>
        public virtual Part LeftMostLocal
        {
            get
            {
                Part left = this;
                Module parent = ParentModule;
                while (left._Left != null && left._Left != this && parent.IsParentOf(left._Left))
                {
                    left = left._Left;
                }
                return left;
            }
        }
        /// <summary>
        /// Returns the left-most part (might be in a different module)
        /// </summary>
        public virtual Part LeftMostGlobal
        {
            get
            {
                Part left = this;
                while (left._Left != null && left._Left != this)
                {
                    left = left._Left;
                }
                return left;
            }
        }
        /// <summary>
        /// Returns the right-most part such that that part is still contained within this part's module
        /// </summary>
        public virtual Part RightMostLocal
        {
            get
            {
                Part right = this;
                Module parent = ParentModule;
                while (right._Right != null && right._Right != this && parent.IsParentOf(right._Right))
                {
                    right = right._Right;
                }
                return right;
            }
        }
        /// <summary>
        /// Returns the right-most part (might be in a different module)
        /// </summary>
        public virtual Part RightMostGlobal
        {
            get
            {
                Part right = this;
                while (right._Right != null && right._Right != this)
                {
                    right = right._Right;
                }
                return right;
            }
        }
        /// <summary>
        /// Get and set the PoPS in the direction of this part
        /// </summary>
        /// <returns></returns>
        public virtual Edge PoPS
        {
            get
            {
                if (_Direction == Orientation.reverse)
                    return PoPS_Left;
                else
                    return PoPS_Right;
            }
            set
            {
                if (_Direction == Orientation.reverse)
                    PoPS_Left = value;
                else
                    PoPS_Right = value;
            }
        }
        
        /// <summary>
        /// This is the Polymerase Per Second rate across this DNA region from left to right
        /// </summary>
        public virtual Edge PoPS_Left
        {
            get
            {
                return _PoPS_Left;
            }
            set
            {
                if (_PoPS_Left == null)
                {
                    _PoPS_Left = new PoPSRate(this);
                }
                string oldRate = _PoPS_Left.Rate;

                Part part = this;

                while (part != null)
                {
                    if (value is PoPSRate && part.AllowPoPSFlow(Orientation.reverse))
                        part._PoPS_Left = (PoPSRate)value;
                    else
                        if (value != part._PoPS_Left)
                        {
                            if (part._PoPS_Left.Rate != value.Rate)
                                part._PoPS_Left.Rate = value.Rate;
                            part._PoPS_Left.CopyModifiers(value);
                        }

                    if (part._Left != null && part._Left != this && part._Left != part && part._Left.AllowPoPSFlow(Orientation.reverse) && (part._Left.PoPS_Left.RateIsZero || part._Left.PoPS_Left.Rate == oldRate) && part._Left.Left != part)
                        part = part.Left;
                    else
                        break;
                }
            }
        }

        /// <summary>
        /// This is the Polymerase Per Second rate across this DNA region from right to left
        /// </summary>
        public virtual Edge PoPS_Right
        {
            get
            {
                return _PoPS_Right;
            }
            set
            {
                if (_PoPS_Right == null)
                {
                    _PoPS_Right = new PoPSRate(this);                    
                }
                string oldRate = _PoPS_Right.Rate;

                Part part = this;

                while (part != null)
                {
                    if (value is PoPSRate && part.AllowPoPSFlow(Orientation.forward))
                        part._PoPS_Right = (PoPSRate)value;
                    else
                        if (value != part._PoPS_Right)
                        {
                            if (part._PoPS_Right.Rate != value.Rate)
                                part._PoPS_Right.Rate = value.Rate;
                            part._PoPS_Right.CopyModifiers(value);
                        }

                    if (part._Right != null && part._Right != this && part._Right != part && part._Right.AllowPoPSFlow(Orientation.forward) && (part._Right.PoPS_Right.RateIsZero || part._Right.PoPS_Right.Rate == oldRate) && part._Right.Right != part)
                        part = part.Right;
                    else
                        break;
                }
            }
        }

        /// <summary>
        /// the position of this DNA part on the DNA strand
        /// </summary>
        public virtual int Position
        {
            get
            {
                return _Position;
            }
            set
            {
                _Position = value;
            }
        }

        /// <summary>
        /// Direction of this part -- Orientation.forward or Orientation.reverse
        /// </summary>
        public virtual Orientation Direction
        {
            get { return _Direction; }
            set
            {
                if (value != _Direction && _Sequence != null)
                    _Sequence = ReverseComplement(_Sequence);

                if (value > 0)
                {
                    _Direction = Orientation.forward;
                    if (_PoPS_Right != null && _PoPS_Left != null)
                        _PoPS_Right.Modifiers = _PoPS_Left.Modifiers;
                }
                else
                {
                    _Direction = Orientation.reverse;
                    if (_PoPS_Left != null && _PoPS_Right != null)
                        _PoPS_Left.Modifiers = _PoPS_Right.Modifiers;
                }
                if (_PoPS_Left == _PoPS_Right && _PoPS_Right != null)
                {
                    if (_Left != null && _Direction == Orientation.reverse)
                        _PoPS_Right = _Left._PoPS_Right;
                    else
                        if (_Right != null && _Direction == Orientation.forward)
                            _PoPS_Left = _Right._PoPS_Left;
                        else
                            if (_Left != null)
                                _PoPS_Right = _Left._PoPS_Right;
                            else
                                if (_Right != null)
                                    _PoPS_Left = _Right._PoPS_Left;
                                else
                                    PoPS = new PoPSRate(this);
                }
            }
        }

        /// <summary>
        /// set right and left to null and call's Node's remove
        /// </summary>
        public override void Remove()
        {
            base.Remove();
            Left = null;   //disconnect
            Right = null;
        }

        /// <summary>
        /// Copy the PoPS rate to all the outgoing edges
        /// </summary>
        /// <param name="edge"></param>
        public void CopyPoPStoEdge(Edge edge)
        {
            if (_Direction == Orientation.forward)
            {
                if (edge != _PoPS_Right)
                {
                    edge.Modifiers = (PoPS_Right.Modifiers);
                    edge.Rate = (PoPS_Right.Rate);
                }
            }
            else
            {
                if (edge != _PoPS_Left)
                {
                    edge.Modifiers = (PoPS_Left.Modifiers);
                    edge.Rate = (PoPS_Left.Rate);
                }
            }
        }

        /// <summary>
        /// Copy the PoPS rate to all the outgoing edges
        /// </summary>
        /// <param name="edge"></param>
        public void CopyPoPStoEdge()
        {
            foreach (Edge edge in _OutgoingEdges)
                CopyPoPStoEdge(edge);
        }

        /// <summary>
        /// Add input edge and set the PoPS to that edge's rate
        /// </summary>
        /// <param name="edge"></param>
        public override void AddIncomingEdge(Edge edge)
        {
            if (edge == _PoPS_Left || edge == _PoPS_Right) return;
            base.AddIncomingEdge(edge);
            if (!PoPS.RateIsZero) 
                edge.Rate = PoPS.Rate;
            else
                PoPS = edge;
            PoPS.Update();
        }

        /// <summary>
        /// Remove input edge and update PoPS
        /// </summary>
        /// <param name="edge"></param>
        public override void RemoveIncomingEdge(Edge edge)
        {
            base.RemoveIncomingEdge(edge);

            PoPS.Update();
            //PoPS.Rate = "0";
            /*foreach (Edge e in _IncomingEdges)
            {
                PoPS.Modifiers.AddRange(e.Inputs);
                PoPS.Modifiers.AddRange(e.Modifiers);
                PoPS.Rate = e.Rate;
            }*/
        }

        /// <summary>
        /// Add the output edge and update its rate
        /// </summary>
        /// <param name="edge"></param>
        public override void AddOutgoingEdge(Edge edge)
        {
            if (_OutgoingEdges != null && _OutgoingEdges.Count > 0)
                RemoveAllOutgoingEdges();

            base.AddOutgoingEdge(edge);
            //CopyPoPStoEdge(edge);
            edge.Rate = ID;
        }
        public override List<Edge> IncomingEdges
        {
            get
            {
                return base.IncomingEdges;
            }
            set
            {
                //base.IncomingEdges = value;
            }
        }
        /// <summary>
        /// All inputs must be species. Part->part is illogical
        /// </summary>
        /// <param name="inputs"></param>
        /// <param name="outputs"></param>
        /// <returns></returns>
        public override bool ValidReactant(ICollection<Node> inputs, ICollection<Node> outputs)
        {
            foreach (Node n in outputs)
            {
                if (!(n is Species))
                {
                    LastErrorMessage = "A part's output must be a species";
                    return false;
                }
            }
            return base.ValidReactant(inputs, outputs);
        }

        /// <summary>
        /// All outputs must be species. Part -> part is illogical
        /// </summary>
        /// <param name="inputs"></param>
        /// <param name="outputs"></param>
        /// <returns></returns>
        public override bool ValidProduct(ICollection<Node> inputs, ICollection<Node> outputs)
        {
            foreach (Node n in inputs)
            {
                if (!(n is Species))
                {
                    LastErrorMessage = "A part's input must be a species";
                    return false;
                }
            }
            return base.ValidProduct(inputs,outputs);
        }

        /// <summary>
        /// Get the reverse complement of a sequence
        /// </summary>
        /// <param name="sequence"></param>
        /// <returns></returns>
        public static string ReverseComplement(string sequence)
        {
            StringBuilder str = new StringBuilder();

            for (int i = sequence.Length - 1; i >= 0; --i)
            {
                switch (sequence[i])
                {
                    case 'A':
                        str.Append('T');
                        break;
                    case 'a':
                        str.Append('t');
                        break;
                    case 'C':
                        str.Append('G');
                        break;
                    case 'c':
                        str.Append('g');
                        break;
                    case 'G':
                        str.Append('C');
                        break;
                    case 'g':
                        str.Append('c');
                        break;
                    case 'T':
                        str.Append('A');
                        break;
                    case 't':
                        str.Append('a');
                        break;
                    default:
                        str.Append('N');
                        break;
                }
            }
            return str.ToString();
        }

        /// <summary>
        /// Writes the XML for this class
        /// </summary>
        /// <param name="writer">xml writer</param>
        public override void WriteXML(XmlWriter writer)
        {
            base.WriteXML(writer);

            Module root = Root as Module;

            Hashtable allitems = null;
            if (root != null) allitems = root.AllItemsWithPrefix;

            writer.WriteStartElement("Part", "");

            writer.WriteStartElement("Direction", "");
            writer.WriteString(Direction.ToString());
            writer.WriteEndElement();

            if (Left != null)
            {
                writer.WriteStartElement("Left", "");
                if (root != null)
                    writer.WriteString(root.Rename(Left.ID, Left.Parent, allitems, false));
                else
                    writer.WriteString(Left.ID);
                writer.WriteEndElement();
            }

            if (Right != null)
            {
                writer.WriteStartElement("Right", "");
                if (root != null)
                    writer.WriteString(root.Rename(Right.ID, Right.Parent, allitems, false));
                else
                    writer.WriteString(Right.ID);
                writer.WriteEndElement();
            }
            if (PoPS_Left != null && !(Right != null && Right.PoPS_Left == PoPS_Left))
            {
                writer.WriteStartElement("PoPS_Left", "");
                PoPS_Left.WriteXML(writer);
                writer.WriteEndElement();
            }

            if (PoPS_Right != null && !(Left != null && Left.PoPS_Right == PoPS_Right))
            {
                writer.WriteStartElement("PoPS_Right", "");
                PoPS_Right.WriteXML(writer);
                writer.WriteEndElement();
            }

            if (string.IsNullOrEmpty(Sequence))
            {
                writer.WriteStartElement("Sequence", "");
                writer.WriteString(Sequence);
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        /// <summary>
        /// Reads the information from an XML file
        /// </summary>
        /// <param name="reader">xlm reader</param>
        public override bool ReadXML(XmlReader reader)
        {
            Edge.AUTO_MODIFIERS = false;

            bool read = base.ReadXML(reader);

            if (!read) return false;

            Module module = Root as Module;
            
            if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "Part"))
                read = reader.Read();

            if (reader.NodeType == XmlNodeType.Element && reader.Name == "Part")
            {
                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;
                    
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "Direction":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string dir = reader.ReadContentAsString();
                                    if (dir == Orientation.forward.ToString())
                                        Direction = Orientation.forward;
                                    else
                                        Direction = Orientation.reverse;
                                }
                                break;
                            case "Sequence":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    Sequence = reader.ReadContentAsString();
                                }
                                break;
                            case "Left":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string id = reader.ReadContentAsString();
                                    if (module != null)
                                    {
                                        Part part = module.FindItem(id,module.AllItemsWithPrefix) as Part;
                                        if (part != null)
                                            Left = part;
                                    }
                                }
                                break;
                            case "Right":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string id = reader.ReadContentAsString();
                                    if (module != null)
                                    {
                                        Part part = module.FindItem(id,module.AllItemsWithPrefix) as Part;
                                        if (part != null)
                                            Right = part;
                                    }
                                }
                                break;
                            case "PoPS_Left":
                                if (PoPS_Left == null)
                                    PoPS_Left = new PoPSRate(this);
                                PoPS_Left.ReadXML(reader);
                                read = reader.Read();
                                break;
                            case "PoPS_Right":
                                if (PoPS_Right == null)
                                    PoPS_Right = new PoPSRate(this);
                                PoPS_Right.ReadXML(reader);
                                read = reader.Read();
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }

                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Part")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }
                Edge.AUTO_MODIFIERS = true;
                return true;
            }
            Edge.AUTO_MODIFIERS = true;
            return false;
        }

        /// <summary>
        /// Connect all these parts together in the given order
        /// </summary>
        /// <param name="partsToConnect">the array of parts to connect in order of left to right</param>
        public static void Connect(params Part[] partsToConnect)
        {
            if (partsToConnect.Length < 2) return;

            for (int i = 0; i < partsToConnect.Length - 1; ++i)
                partsToConnect[i].Right = partsToConnect[i + 1];

        }
    }
}